'''
Created on Oct 19, 2012

@author: Cihat Basol
'''

import os
import sys
import time
import shutil

from PyQt4.QtGui import *
from PyQt4.QtCore import QSettings, QVariant, QSize, QPoint, qDebug, Qt, \
                         SIGNAL, SLOT, QString, QSignalMapper


import dexen
from dexen.system import client
from dexen.system.client import db, ui
from dexen.system.client.controller import db_syncher
from dexen.system.client.controller import action_mgr
from dexen.system.common.excepts import DexenConnectionError
from dexen.system.client.ui.dialogs.new_job_dlg import NewJobDialog
from dexen.system.client.ui.local.localsite import LocalSiteWdg
from dexen.system.client.ui.remote.remotesite import RemoteSiteWdg

# globally used by all the modules in gui
svr_proxy = client.GetServerProxy()

_mainWindow = None

def GetMainWindow():
    global _mainWindow
    if _mainWindow is None:
        _mainWindow = MainWindow()
    return _mainWindow


class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setGeometry(200, 200, 800, 600)
        self.setWindowTitle(QString("Document[*] - Dexen Job Manager"))
        self.db_syncher = db_syncher.GetDBSyncher()
        self._create_widgets()
        self._restore_settings()
        self._create_actions()
        self._create_menus()
        self.current_file = None
        self.dirty = False

    def _create_widgets(self):
        self.localsiteWdg = LocalSiteWdg()
        self.remotesiteWdg = RemoteSiteWdg()
        self.jobinfoTabWdg = ui.GetJobInfoTabWdg()
        
        self.splitter = QSplitter(Qt.Horizontal)
        self.splitter.addWidget(self.localsiteWdg)
        self.splitter.addWidget(self.remotesiteWdg)
                
        self.setCentralWidget(self.splitter)

    def _create_action(self, text, slot=None, shortcut=None, icon=None,
                      tip=None, checkable=False, separator=False, signal="triggered()"):
        action = QAction(text, self)
        if icon is not None:
            action.setIcon(QIcon(":/%s.png" % icon))
        if shortcut is not None:
            action.setShortcut(shortcut)
        if tip is not None:
            action.setToolTip(tip)
            action.setStatusTip(tip)
        if slot is not None:
            self.connect(action, SIGNAL(signal), slot)
        if checkable:
            action.setCheckable(True)
        if separator:
            action.setSeparator(True)

        return action
    
    def _create_actions(self):
        # File menu actions
        self.newFileAct = self._create_action("&New", self.new_file, QKeySequence.New)
        self.openFileAct = self._create_action("&Open...", self.open_file, QKeySequence.Open)
        self.saveFileAct = self._create_action("&Save", self.save_file, QKeySequence.Save)
        self.saveAsFileAct = self._create_action("&Save As", self.saveAs_file, QKeySequence.SaveAs)
        self.exitAct = self._create_action("&Exit", self.exit)
        self.separatorAct = self._create_action("", separator=True)

        # Server menu actions
        self.connectAct = self._create_action("Connect", self.connect_server)
        self.disconnectAct = self._create_action("Disconnect", self.disconnect_server)

        # Server/Job actions
        self.addJobAct = self._create_action("Add Job...", self.add_job)
        self.stopJobAct = self._create_action("Stop Job", self.stop_job)
        self.deleteRemoteJobAct = self._create_action("Delete Job on Server", self.delete_remote_job)
        self.deleteLocalJobAct = self._create_action("Delete Job Data", self.delete_local_job)
        #self.deleteAllLocalJobAct = self._create_action("Delete All Jobs", self.delete_all_local_jobs)

        # Synch actions
        self.synchDataAct = self._create_action("Refresh Job Data", self.synch_data)
        
        # Plugins actions
        self.pluginActs = []
        folder_names = dexen.get_plugin_folders()
        for folder_name in folder_names:
            act = self._create_action(folder_name, self.toggle_custom_tab, checkable=True, signal="toggled(bool)")
            act.folder_name = folder_name
            self.pluginActs.append(act)

    @property
    def dirty(self):
        return self._dirty
    
    @dirty.setter
    def dirty(self, value):
        self._dirty = value
        self.setWindowModified(value)

    def synch_data(self):
        #qDebug("MainWindow::synch_data")
        
        job_info = self.localsiteWdg.current_job()
        job_proxy = svr_proxy.get_job_proxy(job_info)
        act_mgr = action_mgr.GetActionMgr(job_info)

        try:
            self.localsiteWdg.update_jobs(svr_proxy.get_job_infos())
        except DexenConnectionError:
            self.on_disconnect(job_info.get_svr_info())
            return
        
        num_new_actions = job_proxy.num_new_actions()
        if num_new_actions == 0: return
        
        progress = QProgressDialog("Synching job data...", "Stop",
                                   0, num_new_actions, self)
        progress.setWindowTitle("Synch Data")
        progress.setWindowModality(Qt.WindowModal)

        print "%d new actions" % num_new_actions
        i = 0
        all_actions = []
        step = int(num_new_actions / 100) + 1
        step = num_new_actions / 1000
        step = min(step, 500)
        step = max(step, 100)
        status = 0 #SUCCESS
        while i < num_new_actions:
            progress.setValue(i)
            if progress.wasCanceled():
                status = 1 #CANCELED
                break
            result = job_proxy.synch_new_actions(step)
            actions = result["new_actions"]
            updated_inds = result["updated_inds"]
            self.jobinfoTabWdg.on_actions_received(job_info, actions)
            self.jobinfoTabWdg.on_inds_updated(job_info, updated_inds)
            self.db_syncher.persist_actions(job_info, actions, commit=False)
            i += len(actions)
            all_actions.extend(actions)
            print "%d is received" % (len(all_actions))
        self.db_syncher.commit()
        progress.setValue(num_new_actions)
        self.dirty = True
        return status
        
    def on_cur_local_job_changed(self, job_info):
        self.jobinfoTabWdg.on_cur_job_changed(job_info)
    
    def on_jobs_info_updated(self, job_info_list):
        self.jobinfoTabWdg.on_jobs_info_updated(job_info_list)

    def add_job(self):
        newJobDlg = NewJobDialog(self)
        newJobDlg.exec_()
        time.sleep(1)
        self.remotesiteWdg.refresh()

    def delete_remote_job(self):
        job_info = self.remotesiteWdg.current_job()
        svr_proxy.delete_job(job_info)
        self.remotesiteWdg.refresh()

    def delete_local_job(self):
        job_info = self.localsiteWdg.current_job()
        qDebug("In delete_local_job job_info:%s"%(job_info,))
        self.localsiteWdg.delete_job(job_info)
        action_mgr.delete_job(job_info)
        self.db_syncher.shrink()

    def stop_job(self):
        job_info = self.remotesiteWdg.current_job()
        svr_proxy.stop_job(job_info)
        self.remotesiteWdg.refresh()

    def close_file(self):
        db.clear()
        self.db_syncher.close()
        action_mgr.clear()
        self.localsiteWdg.on_close_file()
        self.jobinfoTabWdg.on_close_file()
        self.setWindowTitle(QString("Document[*] - Dexen Job Manager"))

    def new_file(self):
        if not self.ok_to_continue():
            return
        self.close_file()
        self.dirty = False
        self.current_file = None
    
    def open_file(self):
        if not self.ok_to_continue():
            return
        
        fname = QFileDialog.getOpenFileName(self, "%s - Choose Dexen Jobs Data File" % \
                                            QApplication.applicationName())
        if fname is None or not os.path.exists(fname): return
        self.current_file = fname
        
        self.close_file()
        shutil.copyfile(fname, db.CLIENT_DB)

        svr_info_list = self.db_syncher.get_server_info_list()
        self.localsiteWdg.add_servers(svr_info_list)
        # Retrieve actions from DB
        for svr_info in svr_info_list:
            assert isinstance(svr_info, db.ServerInfo)
            for job_info in svr_info.get_jobs():
                actions = self.db_syncher.get_actions(job_info)
                act_mgr = action_mgr.GetActionMgr(job_info)
                act_mgr.process_actions(actions)
                self.jobinfoTabWdg.on_actions_received(job_info, actions, islocal=True)
                self.jobinfoTabWdg.on_inds_updated(job_info, act_mgr.get_last_updated_inds(), islocal=True)
        self.dirty = False
        self.setWindowTitle(QString("%1[*] - Dexen Job Manager").arg(self.current_file))

    def save_file(self):
        if self.current_file is None:
            self.saveAs_file()
        else:
            shutil.copyfile(db.CLIENT_DB, self.current_file)
            self.dirty = False
    
    def saveAs_file(self):
        fname = unicode(QFileDialog.getSaveFileName(self, "Save as - Dexen Jobs Data File"))
        if fname:
            if "." not in fname:
                fname += ".dxn"
            self.current_file = fname
            self.setWindowTitle(QString("%1[*] - Dexen Job Manager").arg(self.current_file))
            self.save_file()
    
    def ok_to_continue(self):
        if self.dirty:
            reply = QMessageBox.question(self, "Save", "Save unsaved changes?",
                                         QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
            if reply == QMessageBox.Cancel:
                return False
            elif reply == QMessageBox.Yes:
                self.save_file()
        return True

    def connect_server(self, svr_addr):
        qDebug("I am connecting to the server")
        try:
            self.setCursor(QCursor(Qt.WaitCursor))
            svr_proxy.connect(svr_addr)
            #start_time = svr_proxy.get_start_time()
        except:
            import traceback
            traceback.print_tb(sys.exc_info()[2])
            print "Connection failed"
            msgBox = QMessageBox(self)
            msgBox.setWindowTitle("Connection failure")
            msg = "Cannot access the dexen server.\n"
            msg += "Make sure the address is valid and the server is running." 
            msgBox.setText(msg)
            msgBox.exec_()
        else:
            self.localsiteWdg.on_connect()
            self.remotesiteWdg.on_connect()
            self.synchDataAct.setEnabled(True)
        finally:
            self.setCursor(QCursor(Qt.ArrowCursor))    
    
    def on_disconnect(self, svr_info):
        self.disconnect_server()
    
    def disconnect_server(self):
        svr_proxy.disconnect()
        #self.connectAct.setEnabled(True)
        #self.disconnectAct.setEnabled(False)
        self.synchDataAct.setEnabled(False)
        self.localsiteWdg.on_disconnect()
        self.remotesiteWdg.on_disconnect()

    def exit(self):
        QApplication.quit()

    def _create_menus(self):
        self.fileMenu = self.menuBar().addMenu("&File")
        self.pluginsMenu = self.menuBar().addMenu("&Plugins")
        self.helpMenu = self.menuBar().addMenu("&Help")
        
        self.fileMenu.addActions([self.newFileAct, self.openFileAct,
                                  self.saveFileAct, self.saveAsFileAct,
                                  self.separatorAct, self.exitAct])
        self.pluginsMenu.addActions(self.pluginActs)

    def toggle_custom_tab(self, checked):
        folder_name = self.sender().folder_name
        self.localsiteWdg.toggle_tab(folder_name, checked)

    def _save_settings(self):
        # Save user settings
        self.localsiteWdg.save_settings()
        self.remotesiteWdg.save_settings()
        settings = QSettings()
        settings.setValue("MainWindow/Size", self.size())
        settings.setValue("MainWindow/Position", self.pos())
        settings.setValue("MainWindow/State", self.saveState())
        settings.setValue("MainWindow/Splitter", self.splitter.saveState())
        
    def _restore_settings(self):
        # Restore user settings
        self.localsiteWdg.restore_settings()
        self.remotesiteWdg.restore_settings()
        settings = QSettings()
        size = settings.value("MainWindow/Size",
                              QVariant(QSize(600, 400))).toSize()
        self.resize(size)
        pos = settings.value("MainWindow/Position",
                             QVariant(QPoint(200, 200))).toPoint()
        self.move(pos)
        self.restoreState(settings.value("MainWindow/State").toByteArray())
        self.splitter.restoreState(settings.value("MainWindow/Splitter").toByteArray())
    
    def closeEvent(self, event):
        qDebug("MainWindow:closeEvent")
        if self.ok_to_continue():
            from dexen.system.client import settings_db
            settings_db.close()
            self._save_settings()
        else:
            event.ignore()

